package io.traveller.apiGateway.client.discovery;

import com.alibaba.fastjson.JSON;
import io.traveller.apiGateway.client.route.RouteExt;
import io.traveller.apiGateway.client.route.RouteURLExt;
import io.traveller.apiGateway.client.util.Utils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * RouteExt定位
 * 参考{@link DiscoveryClientRouteExtLocator}实现。
 *
 * Created by yunai on 16/9/12.
 */
public class DiscoveryClientRouteExtLocator {

    private Logger logger = LoggerFactory.getLogger(DiscoveryClientRouteExtLocator.class);

    @Autowired
    @SuppressWarnings("SpringJavaAutowiringInspection")
    private ZuulProperties zuulProperties;
    @Autowired
    @SuppressWarnings("SpringJavaAutowiringInspection")
    private CuratorFramework curator;

    private AtomicReference<Map<String, RouteExt>> routes = new AtomicReference<>();

    private PathMatcher pathMatcher = new AntPathMatcher();

    public void refresh() {
        Map<String, RouteExt> result = new HashMap<>();
        for (ZuulProperties.ZuulRoute route : zuulProperties.getRoutes().values()) {
            try {
                // 校验route信息
//                if (route.isStripPrefix()) {
//                    throw new RuntimeException(String.format("zuul.route(%s)tripPrefix不能为true.", route.getId()));
//                }
                // 获取routeExt信息
                RouteExt routeExt = getRouteExt(route.getServiceId());
                if (routeExt == null) {
                    throw new RuntimeException(String.format("zuul.route(%s)不存在routeExt.", route.getId()));
                }
                result.put(routeExt.getServiceId(), routeExt);
                for (RouteURLExt urlExt : routeExt.getUrls()) {
                    urlExt.setRouteExt(routeExt);
                }
                Utils.validRouteExt(routeExt);
                // 校验contextPath，防止路由不到
//                if (!pathMatcher.match(route.getPath(), routeExt.getContextPath())) {
//                    throw new RuntimeException(String.format("zuul.route(%s)的path(%s)与routeExt.contextPath(%s)不匹配.",
//                            route.getId(), route.getPath(), routeExt.getContextPath()));
//                }
            } catch (Exception e) {
                // TODO [route模块] 需要增加获取失败时， email通知管理员。
//                e.printStackTrace();
                logger.error("[refresh][加载失败，异常：{}]", e);
                return;
            }
        }
        routes.set(result);
        logger.info("[refresh][加载成功]");
    }

    private RouteExt getRouteExt(String serviceId) {
        String fullServiceId = Utils.getFullServiceId(serviceId);
        for (int i = 0; i < 3; i ++) {
            try {
                String value = new String(curator.getData().forPath(fullServiceId));
                return JSON.parseObject(value, RouteExt.class);
            } catch (KeeperException e) {
                if (e.code() == KeeperException.Code.NONODE) {
                    return null;
                }
            } catch (Exception ignore) {
            }
        }
        throw new RuntimeException(String.format("zuul.route(%s) 读取失败", serviceId));
    }

    public RouteURLExt getMatchingRouteURLExt(String serviceId, String path, String method) {
        RouteExt routeExt = routes.get().get(serviceId);
        if (routeExt == null) {
            return null;
        }
        // 处理url
        if (!"/".equals(routeExt.getContextPath())) {
            path = path.replace(routeExt.getContextPath(), "");
        }
        // 处理方法
        List<RouteURLExt> routeURLExts = getRouteURLExts(routeExt, method);
        // 获取RouteURLExt TODO 优化，增加cache机制
        RouteURLExt routeURLExt = getMatchingRouteURLExtByDirect(routeURLExts, path);
        if (routeURLExt != null) {
            return routeURLExt;
        }
        routeURLExt = getMatchingRouteURLExtByPattern(routeURLExts, path);
        if (routeURLExt != null) {
            return routeURLExt;
        }
        return null;
//        throw new RuntimeException("找不到合适的配置！"); // TODO 需要优化 404????
    }

    // TODO 之后需要做下寻找算法的优化
    private List<RouteURLExt> getRouteURLExts(RouteExt routeExt, String method) {
        List<RouteURLExt> result = new ArrayList<>(routeExt.getUrls().size());
        for (RouteURLExt routeURLExt : routeExt.getUrls()) {
            if (method.equals(routeURLExt.getMethod())) {
                result.add(routeURLExt);
            }
        }
        return result;
    }

    private RouteURLExt getMatchingRouteURLExtByDirect(List<RouteURLExt> routeURLExts, String path) {
        for (RouteURLExt routeURLExt : routeURLExts) {
            if (routeURLExt.getUrl().equals(path)) {
                return routeURLExt;
            }
        }
        return null;
    }

    private RouteURLExt getMatchingRouteURLExtByPattern(List<RouteURLExt> routeURLExts, String path) {
        for (RouteURLExt routeURLExt : routeURLExts) {
            if (pathMatcher.match(routeURLExt.getUrl() + ".*", path)) {
                return routeURLExt;
            }
        }
        return null;
    }

}
